Skip to content

fix(discord-bridge): honor SUTANDO_WORKSPACE for REPO resolution - #708

Merged
sonichi merged 2 commits into
mainfrom
fix/discord-bridge-workspace
May 15, 2026
Merged

sonichi merged 2 commits into
mainfrom
fix/discord-bridge-workspace

Conversation

@qingyun-wu

@qingyun-wu qingyun-wu commented May 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • src/discord-bridge.py: when SUTANDO_WORKSPACE is set, resolve REPO from that path instead of Path(__file__).resolve().parent.parent. Fixes the bundle/workspace divergence that stranded owner DMs on 2026-05-14 — bridge wrote tasks to bundle-tasks/ while core watched workspace-tasks/.
  • .env.example: document when to set SUTANDO_WORKSPACE (app-bundle installs where src/ is symlinked; leave unset for normal git-clone).

Test plan

  • Set SUTANDO_WORKSPACE in .env, restart bridge, DM the bot → task lands in the workspace tasks/, not the bundle copy
  • Unset SUTANDO_WORKSPACE, restart bridge → falls back to the previous Path(__file__).resolve().parent.parent behavior

qingyun-wu and others added 2 commits May 14, 2026 21:21
When src/ is a symlink into a packaged Sutando.app bundle,
Path(__file__).resolve().parent.parent returns the bundle root rather
than the user's workspace. That stranded owner DMs on 2026-05-14 —
bridge wrote tasks to bundle-tasks/ while core watched workspace-tasks/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to the discord-bridge fix — clarifies that this var is only
needed for app-bundle installs where src/ is symlinked, not a normal
git-clone setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chetanunadkat chetanunadkat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix looks right — the diff is small, the fallback preserves the existing behavior for non-bundle installs, and the rationale comment captures the failure mode well.

One follow-up needed

src/telegram-bridge.py:16 has the identical pattern:

REPO = Path(__file__).resolve().parent.parent

So Telegram-bridge will hit the same bundle/tasks/ vs workspace/tasks/ divergence under the app-bundle install. The fix from this PR should be ported there in the same commit (or a follow-up PR explicitly noting it). Otherwise the next "owner DM stranded" report will be Telegram-flavored.

Quick grep confirms no other bridge has this pattern — voice-agent.ts, task-bridge.ts, whatsapp-bridge.ts resolve paths differently and don't appear affected.

Minor nits (optional)

  1. import os placement: it's added inline at line ~25 after import discord. Conventionally goes with the stdlib imports near the top. Doesn't affect behavior, just consistency.

  2. .env.example missing trailing newline: the diff shows \ No newline at end of file at the EOF. Worth re-saving with a final newline so future appends don't sneak onto the same line.

  3. Silent fallback on bad paths: if SUTANDO_WORKSPACE=/typo/path is set, Path(_workspace_env) succeeds and the bridge proceeds — mkdirSync will create the typo'd dir on first write, and owner DMs will land in /typo/path/tasks/ with no warning. A startup-time if not REPO.exists(): logger.warning(...) would catch this. Not blocking — just helpful when someone makes a typo in .env.

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the core fix — env-var override + fallback is clean and well-commented (the comment block explaining the 2026-05-14 divergence is helpful for future spelunkers).

One scope concern: src/telegram-bridge.py:16 and src/dm-result.py:35 use the same Path(__file__).resolve().parent.parent pattern and would diverge identically under the same app-bundle/symlink setup. If you intend SUTANDO_WORKSPACE to fix workspace routing for the whole bridge surface, the telegram-bridge needs the same treatment — otherwise Telegram users in the same app-bundle deployment will hit the bug again.

Nit: missing newline at end of .env.example (the \\ No newline at end of file in the diff).

Concrete suggestion: either (a) expand this PR to also patch telegram-bridge.py + dm-result.py, or (b) ship as-is and file a follow-up. I'd lean (a) — it's the same one-liner pattern.

— MacBook Sutando bot (cc @sonichi for decision)

@sonichi
sonichi merged commit 7782ecc into main May 15, 2026
1 check passed
@sonichi
sonichi deleted the fix/discord-bridge-workspace branch May 15, 2026 16:18
sonichi added a commit that referenced this pull request May 15, 2026
…720) (#722)

* fix(bridges): honor SUTANDO_WORKSPACE in telegram-bridge + dm-result (#720)

Mirror the PR #708 pattern in the two remaining files that use the same
Path(__file__).resolve().parent.parent REPO resolution. Same app-bundle
symlink bug stranded owner DMs on 2026-05-14; this closes the bridge-side
gap for Telegram users.

Smoke-tested both files: SUTANDO_WORKSPACE=/tmp/x → REPO=/tmp/x; unset
→ workspace fallback unchanged.

Closes #720.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(bridges): strip + expanduser SUTANDO_WORKSPACE (Mini review)

Addresses two non-blocking edges Mini flagged on PR #722 review:
1. Empty-string SUTANDO_WORKSPACE= produced Path("") = Path(".") (cwd
   misroute). Now os.environ.get(..., "").strip() returns "" which
   short-circuits to the workspace fallback.
2. No ~ expansion meant SUTANDO_WORKSPACE=~/work resolved to a literal
   "~/work" directory. Now Path(_workspace_env).expanduser() handles it.

Smoke-tested all 4 cases: unset / "" / "   " / "~/work" / "/tmp/test"
— all resolve correctly.

Same single-line shape; same fallback behavior unchanged for the
git-clone default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi added a commit that referenced this pull request May 15, 2026
Mirror the same two robustness guards from PR #722 (Mini's review)
into discord-bridge.py:
1. Empty/whitespace SUTANDO_WORKSPACE= → falls back to workspace
   default (was: Path("") = Path(".") = cwd misroute).
2. SUTANDO_WORKSPACE=~/work → expands to home (was: literal "~/work").

Same one-line shape change as #722. Smoke-tested 5 cases (unset / "" /
"   " / "~/work" / "/tmp/test") all resolve correctly. Default behavior
unchanged for the git-clone setup that #708 originally targeted.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi pushed a commit that referenced this pull request May 16, 2026
The bridges (discord-bridge.py, telegram-bridge.py, dm-result.py) all
honor $SUTANDO_WORKSPACE for their tasks-dir resolution — see
PRs #708/#720/#722/#723. But watch-tasks-stream.sh was still defaulting
to the repo-relative $(dirname $0)/../tasks, so when Sutando.app sets
SUTANDO_WORKSPACE (which it always does), the bridges write tasks to
the workspace but the watcher polls the repo. Owner DMs land but the
agent never sees them.

Diagnosed live 2026-05-15: three Discord DMs were stranded in
$SUTANDO_WORKSPACE/tasks/ for ~17 min before the workaround (restart
the Monitor with an explicit positional arg).

Resolution priority is now:
  1. explicit positional arg (back-compat)
  2. $SUTANDO_WORKSPACE/tasks (matches bridges)
  3. repo-relative fallback (matches old default for non-workspace setups)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi added a commit that referenced this pull request May 16, 2026
#730)

`src/session-handoff.sh` hard-coded `REPO="$HOME/Desktop/sutando"`. On
machines where the workspace lives elsewhere (e.g. `~/Documents/github/
sutando` — Chi's primary node), the script silently no-ops: the output
redirect on line 78 (`> "$STATE_FILE" 2>/dev/null`) eats the "no such
file or directory" error and PreCompact never produces a snapshot.

Same env-resolution pattern that #708 / #720+#722 / #723 already
established for the Discord/Telegram bridges. One-line change.

CLAUDE.md ("Session Continuity") explicitly relies on this file:
"Read this file at session start to understand what the previous
session was doing." With the bug, that signal is dead on any host
that doesn't use `~/Desktop/sutando`.

Smoke-tested locally: with `SUTANDO_WORKSPACE=$PWD` the script writes
session-state.md to the expected location and the System Status /
Recent Work / Open PRs / Pending Questions sections all populate.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.com>
liususan091219 added a commit that referenced this pull request May 16, 2026
…s.cwd() (closes #713) (#757)

src/web-client.ts used bare relative paths in three spots:

  state/voice-mode.txt
  tasks/${taskId}.txt           # /paidsubscriptions/scan trigger
  skills/subscription-scanner/state/subscriptions.json   # x2

All three are resolved against `process.cwd()` at read/write time. Under
bundle/launchd/symlink installs where the CWD at process-launch isn't the
workspace root, every relative path either:

  - silently ENOENT on read (voice-mode → wrong default), or
  - writes the trigger task into bundle/tasks/ where the proactive loop
    isn't watching (paidsubscriptions scan stranded).

This is the same failure mode PR #708 just fixed for discord-bridge.

Fix (Pattern A from the issue): add top-of-file REPO_DIR + TASK_DIR +
STATE_DIR + SUBSCRIPTIONS_PATH constants mirroring task-bridge.ts, then
route every workspace-relative path through join(REPO_DIR, ...). REPO_DIR
honors SUTANDO_WORKSPACE env var first and falls back to the script's
parent dir via fileURLToPath(import.meta.url).

In the common case where startup.sh `cd`'s into the workspace before
launching web-client, behavior is unchanged. The fix only matters when CWD
diverges from the workspace.

Pattern B (centralize task-file writes in task-bridge.ts) is the cleaner
endgame but couples this to #695. Pattern A is the contained, no-blocker fix.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…ichi#708)

* fix(discord-bridge): honor SUTANDO_WORKSPACE for REPO resolution

When src/ is a symlink into a packaged Sutando.app bundle,
Path(__file__).resolve().parent.parent returns the bundle root rather
than the user's workspace. That stranded owner DMs on 2026-05-14 —
bridge wrote tasks to bundle-tasks/ while core watched workspace-tasks/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(env): explain when to set SUTANDO_WORKSPACE

Companion to the discord-bridge fix — clarifies that this var is only
needed for app-bundle installs where src/ is symlinked, not a normal
git-clone setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…onichi#720) (sonichi#722)

* fix(bridges): honor SUTANDO_WORKSPACE in telegram-bridge + dm-result (sonichi#720)

Mirror the PR sonichi#708 pattern in the two remaining files that use the same
Path(__file__).resolve().parent.parent REPO resolution. Same app-bundle
symlink bug stranded owner DMs on 2026-05-14; this closes the bridge-side
gap for Telegram users.

Smoke-tested both files: SUTANDO_WORKSPACE=/tmp/x → REPO=/tmp/x; unset
→ workspace fallback unchanged.

Closes sonichi#720.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(bridges): strip + expanduser SUTANDO_WORKSPACE (Mini review)

Addresses two non-blocking edges Mini flagged on PR sonichi#722 review:
1. Empty-string SUTANDO_WORKSPACE= produced Path("") = Path(".") (cwd
   misroute). Now os.environ.get(..., "").strip() returns "" which
   short-circuits to the workspace fallback.
2. No ~ expansion meant SUTANDO_WORKSPACE=~/work resolved to a literal
   "~/work" directory. Now Path(_workspace_env).expanduser() handles it.

Smoke-tested all 4 cases: unset / "" / "   " / "~/work" / "/tmp/test"
— all resolve correctly.

Same single-line shape; same fallback behavior unchanged for the
git-clone default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
Mirror the same two robustness guards from PR sonichi#722 (Mini's review)
into discord-bridge.py:
1. Empty/whitespace SUTANDO_WORKSPACE= → falls back to workspace
   default (was: Path("") = Path(".") = cwd misroute).
2. SUTANDO_WORKSPACE=~/work → expands to home (was: literal "~/work").

Same one-line shape change as sonichi#722. Smoke-tested 5 cases (unset / "" /
"   " / "~/work" / "/tmp/test") all resolve correctly. Default behavior
unchanged for the git-clone setup that sonichi#708 originally targeted.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…chi#736)

The bridges (discord-bridge.py, telegram-bridge.py, dm-result.py) all
honor $SUTANDO_WORKSPACE for their tasks-dir resolution — see
PRs sonichi#708/sonichi#720/sonichi#722/sonichi#723. But watch-tasks-stream.sh was still defaulting
to the repo-relative $(dirname $0)/../tasks, so when Sutando.app sets
SUTANDO_WORKSPACE (which it always does), the bridges write tasks to
the workspace but the watcher polls the repo. Owner DMs land but the
agent never sees them.

Diagnosed live 2026-05-15: three Discord DMs were stranded in
$SUTANDO_WORKSPACE/tasks/ for ~17 min before the workaround (restart
the Monitor with an explicit positional arg).

Resolution priority is now:
  1. explicit positional arg (back-compat)
  2. $SUTANDO_WORKSPACE/tasks (matches bridges)
  3. repo-relative fallback (matches old default for non-workspace setups)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…nichi#722/sonichi#723) (sonichi#730)

`src/session-handoff.sh` hard-coded `REPO="$HOME/Desktop/sutando"`. On
machines where the workspace lives elsewhere (e.g. `~/Documents/github/
sutando` — Chi's primary node), the script silently no-ops: the output
redirect on line 78 (`> "$STATE_FILE" 2>/dev/null`) eats the "no such
file or directory" error and PreCompact never produces a snapshot.

Same env-resolution pattern that sonichi#708 / sonichi#720+sonichi#722 / sonichi#723 already
established for the Discord/Telegram bridges. One-line change.

CLAUDE.md ("Session Continuity") explicitly relies on this file:
"Read this file at session start to understand what the previous
session was doing." With the bug, that signal is dead on any host
that doesn't use `~/Desktop/sutando`.

Smoke-tested locally: with `SUTANDO_WORKSPACE=$PWD` the script writes
session-state.md to the expected location and the System Status /
Recent Work / Open PRs / Pending Questions sections all populate.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.com>
liususan091219 added a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…s.cwd() (closes sonichi#713) (sonichi#757)

src/web-client.ts used bare relative paths in three spots:

  state/voice-mode.txt
  tasks/${taskId}.txt           # /paidsubscriptions/scan trigger
  skills/subscription-scanner/state/subscriptions.json   # x2

All three are resolved against `process.cwd()` at read/write time. Under
bundle/launchd/symlink installs where the CWD at process-launch isn't the
workspace root, every relative path either:

  - silently ENOENT on read (voice-mode → wrong default), or
  - writes the trigger task into bundle/tasks/ where the proactive loop
    isn't watching (paidsubscriptions scan stranded).

This is the same failure mode PR sonichi#708 just fixed for discord-bridge.

Fix (Pattern A from the issue): add top-of-file REPO_DIR + TASK_DIR +
STATE_DIR + SUBSCRIPTIONS_PATH constants mirroring task-bridge.ts, then
route every workspace-relative path through join(REPO_DIR, ...). REPO_DIR
honors SUTANDO_WORKSPACE env var first and falls back to the script's
parent dir via fileURLToPath(import.meta.url).

In the common case where startup.sh `cd`'s into the workspace before
launching web-client, behavior is unchanged. The fix only matters when CWD
diverges from the workspace.

Pattern B (centralize task-file writes in task-bridge.ts) is the cleaner
endgame but couples this to sonichi#695. Pattern A is the contained, no-blocker fix.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants